﻿using System;
using System.Collections.Generic;
using System.Linq;
using Insesoft.Entities;
using Insesoft.Entities.Repository;
using NHibernate;
using NHibernate.Criterion;
using NHibernate.Linq;

namespace InseSoft.UIControllers
{
    public class Controller
    {
        #region Declarations

        public const string IdField = "Id";
        public const string NombreField = "Nombre";

        #endregion

        #region Constructors

        public Controller() : this(new SqlRepository().OpenSession()) { }
        
        public Controller(ISession session)
        {
            Session = session;
            CurrentDateProvider = GetCurrentDate;
        }

        #endregion

        #region Properties

        public ISession Session { get; private set; }

        public Func<DateTime> CurrentDateProvider { get; set; }

        #endregion
        
        #region Methods: Public
        
        public T GetById<T>(int id)
        {
            using (var transaction = Session.BeginTransaction())
            {
                var entity =  Session.Get<T>(id);
                transaction.Commit();
                return entity;
            }
        }
        
        public IEnumerable<T> GetActive<T>() where T : new()
        {
            using (var transaction = Session.BeginTransaction())
            {
                var query = Session.CreateCriteria(typeof(T));
                var entityType = typeof(T);
                if (entityType.GetProperty("Empresa") != null)
                    query.Add(EqOrNull("Empresa", ApplicationSession.Instance.Empresa));
                if (entityType.GetProperty("Activo") != null)
                    query.Add(Restrictions.Eq("Activo", true));
                if (entityType.GetProperty("Nombre") != null)
                    query.AddOrder(new Order("Nombre", true));
                var result = query.List<T>();
                transaction.Commit();
                return result;
            }
        }

        public IEnumerable<T> GetAll<T>() where T : new()
        {
            using (var transaction = Session.BeginTransaction())
            {
                var query = Session.CreateCriteria(typeof(T));
                var entityType = typeof(T);
                if (entityType.GetProperty("Empresa") != null)
                    query.Add(EqOrNull("Empresa", ApplicationSession.Instance.Empresa));
                if (entityType.GetProperty("Nombre") != null)
                    query.AddOrder(new Order("Nombre", true));
                transaction.Commit();
                return query.List<T>();
            }
        }

        public T Save<T>(T entity) where T : new()
        {
            using (var transaction = Session.BeginTransaction())
            {
                Session.SaveOrUpdate(entity);
                transaction.Commit();
                return entity;
            }
        }

        public void Delete<T>(T entity) where T : new()
        {
            using (var transaction = Session.BeginTransaction())
            {
                Session.Delete(entity);
                transaction.Commit();
            }
        }
        
        public TipoCambio GetTipoCambioByDate(DateTime fecha)
        {
            TipoCambio result;
            using (var transaction = Session.BeginTransaction())
            {
                result = (from t in Session.QueryOver<TipoCambio>()
                              where t.Empresa == ApplicationSession.Instance.Empresa
                                    && t.Fecha == fecha
                              orderby t.Id
                              select t).Desc().Take(1).SingleOrDefault();
                transaction.Commit();
            }
            return result;
        }
        
        public T GetOrNew<T>(int id) where T : class, new()
        {
            return GetById<T>(id) ?? new T();
        }

        #endregion

        #region Methods: Non-Public

        private static AbstractCriterion EqOrNull(string property, object value)
        {
            return value == null ? Restrictions.IsNull(property) : Restrictions.Eq(property, value);
        }

        private DateTime GetCurrentDate()
        {
            return Session.CreateSQLQuery("SELECT DATEADD(dd, 0, DATEDIFF(dd, 0, GETDATE()))")
                            .UniqueResult<DateTime>();
        }

        #endregion
    }
}
